#include "kernel/driver/disk.h"
#include "libs/io.h"
#include "kernel/apic/apic.h"
#include "libs/lib.h"
#include "kernel/log/log.h"
char bufferTest[512];
RequestQueue::RequestQueue(/* args */)
{
}

RequestQueue::~RequestQueue()
{
}



BlockBufferNode::BlockBufferNode(/* args */)
{
}

BlockBufferNode::~BlockBufferNode()
{
}


void BlockBufferNode::read(){

	BlockBufferNode* node = DISKMANAGER->reqQueue.inUsing;

	if(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_ERROR)
		LOG_ERROR("read_handler error:%#010x\n",io_in8(PORT_DISK1_ERR_FEATURE));
	else
	{
		port_insw(PORT_DISK1_DATA,node->buffer,256);
	}
	
	node->count--;
	if(node->count)
	{
		node->buffer+=512;
		return;
	}

	DISKMANAGER->endRequest();
}

void BlockBufferNode::write(){

	BlockBufferNode* node = DISKMANAGER->reqQueue.inUsing;
	
	if(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_ERROR)
		LOG_ERROR("write_handler error:%#010x\n",io_in8(PORT_DISK1_ERR_FEATURE));

	node->count--;
	if(node->count)
	{
		node->buffer+=512;
		return;
	}
	DISKMANAGER->endRequest();
	
}

void BlockBufferNode::other(){

	BlockBufferNode* node = DISKMANAGER->reqQueue.inUsing;

	if(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_ERROR)
		LOG_INFO("read_handler:%#010x\n",io_in8(PORT_DISK1_ERR_FEATURE));
	else
		port_insw(PORT_DISK1_DATA,node->buffer,256);
	DISKMANAGER->endRequest();

}





disk::disk(uint64_t number,interruptmanager* inters)
:interrupthandler(number,inters),Device()
{

	struct IO_APIC_RET_entry entry;

	entry.vector = number;
	entry.deliver_mode = APIC_ICR_IOAPIC_Fixed ;
	entry.dest_mode = ICR_IOAPIC_DELV_PHYSICAL;
	entry.deliver_status = APIC_ICR_IOAPIC_Idle;
	entry.polarity = APIC_IOAPIC_POLARITY_HIGH;
	entry.irr = APIC_IOAPIC_IRR_RESET;
	entry.trigger = APIC_ICR_IOAPIC_Edge;
	entry.mask = APIC_ICR_IOAPIC_Masked;
	entry.reserved = 0;

	entry.destination.physical.reserved1 = 0;
	entry.destination.physical.phy_dest = 0;
	entry.destination.physical.reserved2 = 0;

	install(&entry);
	enable();



    // io_out8(PORT_DISK1_ALT_STA_CTL,0);	
    
	// io_out8(PORT_DISK1_ERR_FEATURE,0);
	// io_out8(PORT_DISK1_SECTOR_CNT,0);
	// io_out8(PORT_DISK1_SECTOR_LOW,0);
	// io_out8(PORT_DISK1_SECTOR_MID,0);
	// io_out8(PORT_DISK1_SECTOR_HIGH,0);
	// io_out8(PORT_DISK1_DEVICE,0xe0);
	// io_out8(PORT_DISK1_STATUS_CMD,0xec);	//identify
    


	// io_out8(PORT_DISK1_ALT_STA_CTL,0);
	// while(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_BUSY);

	// LOG_INFO("read one sector starting:",io_in8(PORT_DISK1_STATUS_CMD));

	// io_out8(PORT_DISK1_ERR_FEATURE,0);
	// io_out8(PORT_DISK1_SECTOR_CNT,1); //扇区数
	// io_out8(PORT_DISK1_SECTOR_LOW,0);
	// io_out8(PORT_DISK1_SECTOR_MID,0);
	// io_out8(PORT_DISK1_SECTOR_HIGH,0);
	// io_out8(PORT_DISK1_DEVICE,0xe0);
	
	// while(!(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_READY));

	// io_out8(PORT_DISK1_STATUS_CMD,0x20);	////read







}

disk::~disk()
{
	disable();
	uninstall();
}


int64_t disk::ioctl(int64_t cmd,int64_t arg){

	BlockBufferNode* node = NULL;
	if(cmd == GET_IDENTIFY_DISK_CMD)
	{
		node = DISKMANAGER->makeRequest(cmd,0,0,(uint8_t*)arg);
		DISKMANAGER->subRequest(node);
		DISKMANAGER->waitFinish();
		return 1;
	}
	return 0;
}


// void getDeviceInfo(){

// 	int i = 0;
// 	Disk_Identify_Info a;
// 	unsigned short *p = NULL;
// 	port_insw(PORT_DISK1_DATA,&a,256);
	
// 	LOG_INFO("\nSerial Number:");
// 	for(i = 0;i<10;i++)
// 		LOG_INFO("%c%c",(a.Serial_Number[i] >> 8) & 0xff,a.Serial_Number[i] & 0xff);
	
// 	LOG_INFO("\nFirmware revision:");
// 	for(i = 0;i<4;i++)
// 		LOG_INFO("%c%c",(a.Firmware_Version[i] >> 8 ) & 0xff,a.Firmware_Version[i] & 0xff);
	
// 	LOG_INFO("\nModel number:");
// 	for(i = 0;i<20;i++)
// 		LOG_INFO("%c%c",(a.Model_Number[i] >> 8) & 0xff,a.Model_Number[i] & 0xff);
// 	LOG_INFO("\n");

// 	p = (unsigned short *)&a;
// 	for(i = 0;i<256;i++)
// 		LOG_INFO("%04x ",*(p+i));
// }


int64_t disk::transfer (int64_t cmd,uint64_t blocks,int64_t count,uint8_t* buffer){

	BlockBufferNode* node = NULL;
	if(cmd == ATA_READ_CMD || cmd == ATA_WRITE_CMD)
	{
		
		node = DISKMANAGER->makeRequest(cmd,blocks,count,buffer);
		
		DISKMANAGER->subRequest(node);
		
		DISKMANAGER->waitFinish();
		
		
	}else{
		return 0;
	}

	return 1;

}

StackFrame* disk::handleInterrupt(StackFrame* fs){


	BlockBufferNode* node = DISKMANAGER->reqQueue.inUsing;
	
	switch (node->cmd)
	{
		case ATA_READ_CMD/* constant-expression */:
			/* code */
			node->read();

			break;
		case ATA_WRITE_CMD/* constant-expression */:
			/* code */
			node->write();
			break;	
		case GET_IDENTIFY_DISK_CMD:
			node->other();
			break;
		default:
		
		break;
	}



}


DiskManager* DiskManager::diskM = 0;



int DiskManager::diskFlags = 0;
DiskManager::DiskManager(interruptmanager* inters)
{

	
	disks[0] = new disk(0x2e,inters);
	disks[1] = new disk(0x2f,inters);

	diskM = this;
}

DiskManager::~DiskManager()
{

	delete disks[0];
	disks[0] = NULL;
	delete disks[1];
	disks[1] = NULL;

}


BlockBufferNode* DiskManager::makeRequest(int64_t cmd,uint64_t blocks,int64_t count,uint8_t* buffer){

	BlockBufferNode* node = new BlockBufferNode();
	node->tsk = current;
	node->cmd = cmd;
	node->LBA = blocks;
	node->count = count;
	node->buffer = buffer;
	return node;

}


void DiskManager::subRequest(BlockBufferNode* node){



	reqQueue.queueList.push(node);
	reqQueue.blockRequestCount++;

	if(reqQueue.inUsing == NULL)
		handleRequest();
	
}

void DiskManager::handleRequest(){

	
	BlockBufferNode* node = reqQueue.inUsing = reqQueue.queueList.pop();

	reqQueue.blockRequestCount--;

	diskFlags  =  1;
		static int count = 0;
	
	// LOG_ERROR("send %d\n",count++);
	
	io_out8(PORT_DISK0_ALT_STA_CTL,0);
	while(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_BUSY);
	switch (node->cmd)
	{
	case ATA_WRITE_CMD/* constant-expression */:
		/* code */

		io_out8(PORT_DISK1_ALT_STA_CTL,0);
		while(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_BUSY);

		
		io_out8(PORT_DISK1_ERR_FEATURE,0);
		io_out8(PORT_DISK1_SECTOR_CNT,node->count& 0Xff); //扇区数
		io_out8(PORT_DISK1_SECTOR_LOW,node->LBA & 0xff);
		io_out8(PORT_DISK1_SECTOR_MID,(node->LBA >> 8) & 0xff);
		io_out8(PORT_DISK1_SECTOR_HIGH,(node->LBA >> 16) & 0xff);
		io_out8(PORT_DISK1_DEVICE,0xe0 | ((node->LBA >> 24) & 0xf));

		
		while(!(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_READY))
			nop();

		io_out8(PORT_DISK1_STATUS_CMD,ATA_WRITE_CMD);	////write
		
		while(!(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_REQ))
			nop();

		port_outsw(PORT_DISK1_DATA,node->buffer,256);
		break;
		
	case ATA_READ_CMD:
			
	

			io_out8(PORT_DISK1_ALT_STA_CTL,0);
			while(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_BUSY);
			io_out8(PORT_DISK1_ERR_FEATURE,0);
			io_out8(PORT_DISK1_SECTOR_CNT,node->count & 0xff);
			io_out8(PORT_DISK1_SECTOR_LOW,node->LBA & 0xff);
			io_out8(PORT_DISK1_SECTOR_MID,(node->LBA >> 8) & 0xff);
			io_out8(PORT_DISK1_SECTOR_HIGH,(node->LBA >> 16) & 0xff);
			io_out8(PORT_DISK1_DEVICE,0xe0 | ((node->LBA >> 24) & 0xf));
			
			
			while(!(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_READY))
				nop();
			io_out8(PORT_DISK1_STATUS_CMD,ATA_READ_CMD);

		break;

	case GET_IDENTIFY_DISK_CMD:
		
			io_out8(PORT_DISK1_DEVICE,0xe0);
			io_out8(PORT_DISK1_ERR_FEATURE,0);
			io_out8(PORT_DISK1_SECTOR_CNT,node->count & 0xff);
			io_out8(PORT_DISK1_SECTOR_LOW,node->LBA & 0xff);
			io_out8(PORT_DISK1_SECTOR_MID,(node->LBA >> 8) & 0xff);
			io_out8(PORT_DISK1_SECTOR_HIGH,(node->LBA >> 16) & 0xff);

			while(!(io_in8(PORT_DISK1_STATUS_CMD) & DISK_STATUS_READY))
				nop();			
			io_out8(PORT_DISK1_STATUS_CMD,node->cmd);

	default:
		LOG_INFO("ATA CMD Error\n");
		break;
	}

}

void DiskManager::waitFinish(){

	static int count = 0;
	// LOG_ERROR("wait %d\n",count++);
	while(diskFlags){
		
	}
		// current->state = TASK_UNINTERRUPTIBLE;
		// TASKMANAGER->shcedule();

}

void DiskManager::endRequest(){
	
	BlockBufferNode* node = reqQueue.inUsing;
	if(node == NULL)
	{
		return;
	}

	// node->tsk->state = TASK_RUNNING;
	// TASKMANAGER->addTaskToQueue(node->tsk);
	delete reqQueue.inUsing;

	reqQueue.inUsing = NULL;

	diskFlags = 0;
	static int count = 0;
	// LOG_ERROR("end %d\n",count++);

	if(reqQueue.blockRequestCount)
		handleRequest();	
}


uint64_t DiskManager::readDisk(int64_t diskNumber,int64_t startSector,int64_t number,uint8_t* buffer){

	
	return disks[diskNumber]->transfer(ATA_READ_CMD,startSector,number,buffer);

}
uint64_t DiskManager::writeDisk(int64_t diskNumber,int64_t startSector,int64_t number,uint8_t* buffer){

	return disks[diskNumber]->transfer(ATA_WRITE_CMD,startSector,number,buffer);
	
}